﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Collections;
using System.Reflection;

using Idlesoft.Utils;
using System.Globalization;

namespace Idlesoft.Cli
{
	public static class Converters
	{
		public static TypeConverter GetCoverter(Type type)
		{
			TypeConverter converter = null;
			ConstructorInfo ctor = null;
			Type[] types = new Type[] { type.GetElementType() };

			if (type.IsArray)
			{
				ctor = typeof(ArrayConverter<>).MakeGenericType(types).GetConstructor(Type.EmptyTypes);
				converter = ctor.Invoke(null) as TypeConverter;
			}
			else if (type.IsGenericType)
			{
				if (type.Equals(typeof(Nullable<>) ))
				{
					converter = new NullableConverter(type);
				}
				else if (type.Equals(typeof(List<>)))
				{
					ctor = typeof(ListConverter<>).MakeGenericType(types).GetConstructor(Type.EmptyTypes);
					converter = ctor.Invoke(null) as TypeConverter;
				}
				else if (type.Equals(typeof(Dictionary<,>)))
				{
					ctor = typeof(DictionaryConverter<,>).MakeGenericType(types).GetConstructor(Type.EmptyTypes);
					converter = ctor.Invoke(null) as TypeConverter;
				}
			}

			return converter;
		}

		public static bool ParseArray<T>(object inValue, ref object outValue)
		{
			bool result = false;
			outValue = new string[] { inValue as string };
			ArrayList list = new ArrayList();

			// Searches for 1D arrays in one 2D array  ex:{{a,b,c},{d,e}} ==> {a,b,c} and {d,e}
			MatchCollection collection = Regex.Matches(inValue as string, "{[^{]+[^}]}");

			for (int i = 0; i < collection.Count; i++)
			{
				Match match = Regex.Match(collection[i].ToString(), @"(\{ *)(.*)( *\})");
				string s = inValue as string;

				string[] inValues = default(string[]);
				T[] outValues = default(T[]);
				if (match.Success && match.Groups.Count == 4)
				{
					string array = Regex.Replace(match.Groups[2].Value, "^ +| +$", "");
					inValues = Regex.Split(array, ", *");
					outValues = new T[inValues.Length];
				}
				else
				{
					inValues = new string[] { inValue as string };
					outValues = new T[1] { default(T) };
				}
				if (inValues != default(string[]) && outValues != default(T[]))
				{
					Type type = typeof(T);
					TypeConverter converter = null;

					// Currently, ini supports till 2D array. Processing of 1D and 2D arrays are handled seperately
					// Check for type and get rank if it's an array
					if (type.IsArray && type.GetArrayRank() > 0)
					{
						// if 2D array
						converter = TypeDescriptor.GetConverter(type.GetElementType());
						if (converter != null)
						{
							list.Add(inValues);
							outValue = (T[])list.ToArray(typeof(T));
						}
					}
					else
					{
						// if 1D array or single value
						converter = TypeDescriptor.GetConverter(type);
						if (converter != null)
						{
							for (int index = 0; index < inValues.Length; ++index)
								outValues[index] = (T)converter.ConvertFromInvariantString(inValues[index] as string);

							outValue = outValues;
						}
					}
				}
			}

			return result;
		}
		public static bool ParseArray<K, V>(object inValue, ref object outValue)
		{
			bool result = false;
			outValue = inValue;
			Match match = Regex.Match(inValue as string, @"(\{ *)(.*)( *\})");
			if (match.Success)
			{
				throw new NotImplementedException("dictionary support to come");
			}
			return result;
		}
	}
	public class ArrayConverter<T> : TypeConverter
	{

		public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
		{
			if (sourceType == typeof(string))
				return true;
			return base.CanConvertFrom(context, sourceType);
		}
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			return base.CanConvertTo(context, destinationType);
		}
		public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
		{
			string text = value as string;
			object result = null;

			if (text != null)
			{
				try
				{
					Converters.ParseArray<T>(value, ref result);
				}
				catch { throw new ArgumentException("The arguments were not valid in ArrayConverter.ConvertFrom"); }
			}
			return result;
		}
		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
		{
			object result = null;

			TypeCode code = Type.GetTypeCode(destinationType);
			if (code == TypeCode.String)
			{
				object[] values = null;
				if (value.GetType().IsArray)
					values = value as object[];
				else
					values = new object[] { value };

				result = values.Flatten("{ ", ", ", " }");
			}
			else
				base.ConvertTo(context, culture, value, destinationType);
			return result;
		}
	}
	public class ListConverter<T> : ArrayConverter<T>
	{
		public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
		{
			if (sourceType == typeof(string))
				return true;
			return base.CanConvertFrom(context, sourceType);
		}
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			return base.CanConvertTo(context, destinationType);
		}
		public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
		{
			List<T> result = null;
			object array = base.ConvertFrom(context, culture, value);
			if (array != null)
				result = new List<T>(array as IEnumerable<T>);
			return result;
		}
		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
		{
			object result = null;

			TypeCode code = Type.GetTypeCode(destinationType);
			if (code == TypeCode.String)
			{
				if (value.GetType().IsGenericType)
				{
					Guid guid = value.GetType().GUID;
					if ("5af00c2e-048b-3ab3-8ccd-548134771d38" != guid.ToString())
					{
						List<string> list = value as List<string>;
						result = list.Flatten("{ ", ", ", " }");
					}
				}
			}
			else
				base.ConvertTo(context, culture, value, destinationType);
			return result;
		}
	}
	public class DictionaryConverter<K, V> : TypeConverter
	{
		public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
		{
			if (sourceType == typeof(string))
				return true;
			return base.CanConvertFrom(context, sourceType);
		}
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			return base.CanConvertTo(context, destinationType);
		}
		public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
		{
			string text = value as string;
			if (text != null)
			{
				try
				{
					throw new NotImplementedException("dictionary support to come");
				}
				catch { }
				throw new ArgumentException("The arguments were not valid in DictionaryConverter.ConvertFrom");
			}

			return base.ConvertFrom(context, culture, value);
		}
	}
}
